<?php
/**
 * coreRouterParamAbstract
 *
 * 파라미터 관리 추상화 객체
 *
 * @package router
 * @author  Platform Team
 */

abstract class coreRouterParamAbstract
{
    /**
     * 파라미터 키 상수
     */
    const KEY_GET       = 'get';
    const KEY_POST      = 'post';
    const KEY_FILES     = 'files';
    const KEY_REQUEST   = 'request';
    const KEY_SSL       = 'ssl';

    /**
     * Sslwrap 객체
     * @var Sslwrap
     */
    private $Sslwrap;

    /**
     * 파라미터 배열
     * @var array(
     *  'get' => array
     *  'post' => array
     *  'files' => array
     *  'request' => array
     *  'ssl' => array
     * )
     */
    protected $aParam;

    /**
     * 파라미터 배열 원본
     * @var array(
     *  'get' => array
     *  'post' => array
     *  'files' => array
     *  'request' => array
     * )
     */
    protected $aParamOrigin;

    /**
     * 파일 업로드 여부
     * @var boolean
     */
    protected $isUploaded;

    /**
     * 생성자
     *
     * @param Sslwrap $Sslwrap
     */
    final public function __construct($Sslwrap)
    {
        $this->Sslwrap = $Sslwrap;
    }

    /**
     * 파라미터 파싱 추상화 메소드
     * @param array $aParam
     */
    abstract public function parse();

    /**
     * 업로드 파일이 존재하는 지 파악
     *
     * @return boolean
     */
    final public function isUploaded()
    {
        return $this->isUploaded === true ? true : false;
    }

    /**
     * 파라미터가 존재하는지 파악
     *
     * @param string $sKey
     * @param string $sType
     * @return boolean
     */
    final public function isParam($sKey, $sType=null)
    {
        if ($sKey === null) {
            return false;
        }

        if ($sType === null) {
            return isset($this->aParam[self::KEY_REQUEST][$sKey]);
        }

        return isset($this->aParam[$sType][$sKey]);
    }

    /**
     * 파라미터 전체를 반환합니다.
     *
     * @return array
     */
    final public function getParams()
    {
        return $this->aParam;
    }

    /**
     * 원본 파라미터 전체를 반환합니다.
     *
     * @return array
     */
    final public function getParams_()
    {
        return $this->aParamOrigin;
    }

    /**
     * 파라미터 반환합니다. (키가 없으면 전체를 반환)
     *
     * @param string $sKey
     * @param string $sType
     * @return mixed
     */
    final public function getParam($sKey=null, $sType=null)
    {
        return $this->getParamByRef($this->aParam, $sKey, $sType);
    }

    /**
     * 원본 파라미터 반환합니다. (키가 없으면 전체를 반환)
     *
     * @param string $sKey
     * @param string $sType
     * @return mixed
     */
    final public function getParam_($sKey=null, $sType=null)
    {
        return $this->getParamByRef($this->aParamOrigin, $sKey, $sType);
    }

    /**
     * 지정된 파라미터를 할당합니다.
     *
     * @param string $sKey
     * @param mixed $mVal
     * @param string $sType
     * @return boolean
     */
    final public function setParam($sKey, $mVal, $sType=null)
    {
        if (empty($sKey)) return false;

        if ($sType === null) {
            $this->aParam[self::KEY_GET][$sKey] = $mVal;
            $this->aParam[self::KEY_POST][$sKey] = $mVal;
            $this->aParam[self::KEY_REQUEST][$sKey] = $mVal;
        } else {
            $this->aParam[$sType][$sKey] = $mVal;
        }

        return true;
    }

    /**
     * 전체 파라미터에 새로운 파라미터로 대체합니다.
     *
     * @param array $aParam
     */
    final public function overrideParams($aParam)
    {
        $this->aParam = $aParam;
    }

    /**
     * 파라미터 반환
     *
     * @param array $aParam
     * @param string $sKey
     * @param string $sType
     * @return mixed
     */
    private function getParamByRef($aParam, $sKey=null, $sType=null)
    {
        if ($sKey === null && $sType === null) {
            return $aParam[self::KEY_REQUEST];
        }

        if ($sType === null) {
            return $aParam[self::KEY_REQUEST][$sKey];
        }

        if ($sKey === null) {
            return $aParam[$sType];
        }

        return $aParam[$sType][$sKey];
    }

    /**
     * SSL 파라미터 복호화 세팅
     * Frontend API 를 사용하는 암호화된 파라미터들을 자동으로 복호화 하여 할당
     */
    final protected function setParamSsl()
    {
        $this->aParam[self::KEY_SSL] = array();

        if ($this->Sslwrap->isFrontendEnabled() === false) {
            return;
        }

        $sReqKey = $this->Sslwrap->getFrontendEncryptReqKey();
        $sEncrypt = empty($_REQUEST[$sReqKey]) === false ? $_REQUEST[$sReqKey] : false;

        if ($sEncrypt === false) {
            return;
        }

        unset($_REQUEST[$sReqKey], $_GET[$sReqKey], $_POST[$sReqKey]);

        $mParam = $this->Sslwrap->decryptFrontend($sEncrypt);

        if (is_object($mParam) === true) {
            trigger_error('[Router] can not decrypt the parameter key. MSG : ' . $mParam->getMessage(), E_USER_WARNING);
            return;
        }

        $mParam = $this->_sslParamParse($mParam);

        $this->aParam[self::KEY_SSL] = $mParam;
    }

    /**
     * _GET, _POST 데이터 세팅
     */
    final protected function setParamGetPost()
    {
        if (ini_get('magic_quotes_gpc') === '1') {
            $this->aParamOrigin[self::KEY_GET] = $_GET;
            $this->aParam[self::KEY_GET] = stripslashesDeep($_GET);

            $this->aParamOrigin[self::KEY_POST] = $_POST;
            $this->aParam[self::KEY_POST] = stripslashesDeep($_POST);
        } else {
            $this->aParamOrigin[self::KEY_GET] = $_GET;
            $this->aParam[self::KEY_GET] = $_GET;

            $this->aParamOrigin[self::KEY_POST] = $_POST;
            $this->aParam[self::KEY_POST] = $_POST;
        }

        $this->_mergeVariable(
            $this->aParamOrigin[self::KEY_REQUEST],
            $this->aParamOrigin[self::KEY_GET],
            $this->aParamOrigin[self::KEY_POST]
        );
    }

    /**
     * _FILES 데이터 세팅
     */
    final protected function setParamFiles()
    {
        if (!isset($_SERVER['CONTENT_TYPE'])) return;
        if (!preg_match('/multipart\/form-data/i', $_SERVER['CONTENT_TYPE'])) return;
        if (empty($_FILES)) return;

        $this->setUploadFiles($_FILES, $this->aParam[self::KEY_FILES]);
    }

    /**
     * 업로드 파일 세팅
     *
     * !!WARNING!!
     * 아래는 정상적인 경우
     * name="file"
     * name="file1[]"
     * name="file1[]"
     * name="file1[2]"
     * name="file1[5]"
     *
     * 위의 경우 아래와 같이 데이터가 저장된다.
     * array(
     *  'file1' => array(
     *    0=> array(
     *      'name' =>,
     *      'type' =>,
     *      'tmp_name' =>,
     *      'error' =>,
     *      'size' =>
     *    ),
     *    1=> array(
     *      ...
     *    )
     *  )
     * )
     *
     * 아래는 비정상적인 경우
     * name="file2[][][]"
     * name="file2[][][4]"
     */
    private function setUploadFiles($aData, &$aParentData)
    {
        foreach ($aData as $k => $v) {

            if (is_array($v['tmp_name'])) {

                foreach ($v['tmp_name'] as $i => $xx) {

                    if (is_array($v['tmp_name'][$i])) {
                        continue;
                    }

                    $aNestedData = array();
                    $aNestedData[$k] = array(
                        'name' => $v['name'][$i],
                        'type' => $v['type'][$i],
                        'tmp_name' => $v['tmp_name'][$i],
                        'error' => $v['error'][$i],
                        'size' => $v['size'][$i]
                    );

                    if (is_uploaded_file($v['tmp_name'][$i])) {
                        $aParentData[$k][$i] = $aNestedData[$k];
                        $this->isUploaded = true;
                    }

                }

                continue;

            }

            if (is_uploaded_file($v['tmp_name'])) {
                $aParentData[$k] = $v;
                $this->isUploaded = true;
            }

        }
    }

    /**
     * _GET, _POST 데이터 머지
     */
    final protected function mergeGetPost()
    {
        $this->aParam[self::KEY_REQUEST] = $this->aParam[self::KEY_GET];

        $this->_mergeVariable(
            $this->aParam[self::KEY_REQUEST],
            $this->aParam[self::KEY_GET],
            $this->aParam[self::KEY_POST]
        );
    }

    /**
     * _FILES 데이터 머지
     */
    final protected function mergeGetPostFiles()
    {
        if ($this->isUploaded !== true) return;

        $this->_mergeVariable(
            $this->aParam[self::KEY_REQUEST],
            $this->aParam[self::KEY_REQUEST],
            $this->aParam[self::KEY_FILES]
        );
    }

    /**
     * 데이터 머지
     *
     * @param array $aTarget
     * @param array $aVal
     * @param array $aVal2
     */
    private function _mergeVariable(&$aTarget, $aVal, $aVal2)
    {
        $aTarget = $aVal;

        if (empty($aVal2)) return;

        foreach ($aVal2 as $key => $val) {
            $aTarget[$key] = $val;
        }
    }

    /**
     * SSL 파라미터 가공
     *
     * @param array $aData
     * @return array
     */
    private function _sslParamParse($aData)
    {
        if (empty($aData)) {
            return $aData;
        }

        $sPrefix = $this->Sslwrap->getFrontendEncryptReqArrValuePrefix();
        $sSep = $this->Sslwrap->getFrontendEncryptReqArrValueSep();
        $sUnset = $this->Sslwrap->getFrontendEncryptReqUnset();

        foreach ($aData as $k => $v) {
            if ($v === $sUnset) {
                // radio, checkbox 같은 엘리먼트들은 선택이 안되었을 때 빈값이 넘어오도록 되어 있는데
                // 이러한 특성을 가진 엘리먼트들은 기존 GET, POST 항목과 동일하게 세팅이 되도록 제거해준다.
                unset($aData[$k]);
                continue;
            }

            if (strpos($v, $sPrefix) !== 0) {
                continue;
            }

            $aData[$k] = explode($sSep, $v);
            $aData[$k][0] = str_replace($sPrefix, '', $aData[$k][0]);
        }

        return $aData;
    }
}
